﻿using System;
using System.Collections.Generic;
using System.Linq;
using Foxit.PDF;
using Foxit.PDF.Imaging;
using Foxit.PDF.PageElements;
using Sustainalytics.Entities.ProductInvolvement;
using Sustainalytics.Reporting.Framework;
using Sustainalytics.Utils;

namespace Sustainalytics.ReportGenerator.BL
{
    public class TearSheetProductInvolvementSection : PdfReportSectionGenerator
    {
        private const float _sectionNameFontSize = 12f;

        public override void ComposeSectionContent()
        {
            const float startPosition = 130f;

            var page = _report.NewPage();
            _report.Pages.Add(page);


            AddHeaderToPage(page);

            AddProductInvolvement(page, startPosition);
            

            _outlinePositions.Add(new Reporting.Framework.OutlinePosition { Name = "Product Involvement", PageNumber = 1 });
        }

        private void AddProductInvolvement(PdfReportPage page, float top)
        {
            const float textWidth = 180f;


            const string hiffiTitle = "<p><b>HIGH IMPACT FOSSIL FUEL INVOLVEMENT</b></p>";
            const string hiffiHelpText = "<p>This section provides information on which companies have exposure to the carbon - intense fossil fuels of thermal coal and oil sands. Company involvement in thermal coal and oil sands is based on percentage of production, revenue and / or generating capacity. Involvement does not factor into the overall ESG score.</p>";

            const string piTitle = "<p><b>PRODUCT INVOLVEMENT</b></p>";
            const string piHelpText = "<p>This section provides information on the company’s involvement through production, manufacturing, retail sales etc in the following product involvement areas. The involvement is assessed based on revenue derived. The product involvement does not factor into the overall ESG score.</font></p>";
 

            var style = new FormattedTextAreaStyle(Fonts.CalibriFontFamily, 6.5f, false)
            {
                Paragraph = { Align = TextAlign.Justify, Indent = 0, SpacingAfter = 5, AllowOrphanLines = false }
            };

            var left = _report.LeftMargin;


            page.Elements.Add(new FormattedTextArea(hiffiTitle, left, top, textWidth, 40f, Fonts.CalibriFontFamily, 16f, false));
            page.Elements.Add(new FormattedTextArea(hiffiHelpText, left, top + 45f, 180f, 100f, style));

            var table = AddProductInvolvement(top, GetHighImpactFossilFuelInvolvementIndicators());
            page.Elements.Add(table);
            var requiredHeight =  table.GetRequiredHeight();

            if (requiredHeight < 110)
            {
                top += 110f;
            }
            else
            {
                top += requiredHeight;
            }

            page.Elements.Add(new Line(_report.LeftMargin, top, _report.LeftMargin + _report.PageMaxWidth, top, 0.5f, RgbColor.Gray));

            top += 20;

            var piHelpTextArea = new FormattedTextArea(piHelpText, left, top + 30f, 180f, 100f, style);
            var piHelpTextHeight = (int)piHelpTextArea.GetRequiredHeight() + 10f;


            page.Elements.Add(new FormattedTextArea(piTitle, left, top, textWidth, 30f, Fonts.CalibriFontFamily, 16f, false));
            page.Elements.Add(new FormattedTextArea(piHelpText, left, top + 30f, 180f, piHelpTextHeight, style));

            var productInvolvementIndicators = GetProductInvolvementIndicators();

            if (!productInvolvementIndicators.Any()) return;

            var lastEdited = productInvolvementIndicators.Max(o => o.LastEdited);

            if (lastEdited != DateTime.MinValue)
            {
                var lastUpdate = $"Last update {lastEdited:MMM, yyyy}";

                page.Elements.Add(new Label(lastUpdate, left, top + 30f + piHelpTextHeight, textWidth,
                    8f + 2f, Fonts.CalibriRegular, 8f, TextAlign.Left, RgbColor.Black));
            }

            table = AddProductInvolvement(top, productInvolvementIndicators);
            page.Elements.Add(table);

            top = top + table.GetRequiredHeight() + 10f;

            if (top > 640f)
            {
                page = _report.NewPage();
                _report.Pages.Add(page);

                AddHeaderToPage(page);

                top = 130f;
            }

            if (top > 130f)
            {
                page.Elements.Add(new Line(_report.LeftMargin, top, _report.LeftMargin + _report.PageMaxWidth, top, 0.5f,
                    RgbColor.Gray));

                top += 20;
            }

            AddDisclaimers(page, top);

        }

        private void AddDisclaimers(Page page, float top)
        {
            const float aboutTextWidth = 200f;
            const float disclaimerTextWidth = 325f;

            const string aboutTitle = "<p><b>ABOUT SUSTAINALYTICS</b></p>";
            const string disclaimerTitle = "<p><b>DISCLAIMER</b></p>";
            const string aboutText = "<p>Sustainalytics is an award-winning global responsible investment research firm specialized in environmental, social and governance(ESG) research and analysis. The firm offers global perspectives and solutions that are underpinned by local expertise, serving both values - based and mainstream investors that integrate ESG information and assessments into their investment decisions.</p>";

            var disclaimerText = $"<p><b>Copyright ©{DateTime.UtcNow.Year} Sustainalytics. All rights reserved.</b></p><p>Sustainalytics is solely the ESG provider and it does not represent or warrant the appropriateness of the use of any such information for any investment decision and any investment decisions are the sole responsibility of the users of this information. The copying, republication or redistribution of the information contained herein, including by framing, scanning, copying or other similar methods is expressly prohibited without the prior written consent of Sustainalytics. No liability is accepted by Sustainalytics for the accuracy or completeness of the information or software furnished through the platform, or for delays, interruptions or omissions therein nor for any lost profits, indirect, special or consequential damages or any other damages.</p>";


            var style = new FormattedTextAreaStyle(Fonts.CalibriFontFamily, 8f, false)
            {
                Paragraph = { Align = TextAlign.Justify, Indent = 0, SpacingAfter = 5, AllowOrphanLines = false }
            };


            var aboutTextArea = new FormattedTextArea(aboutText, 0.0f, 0.0f, aboutTextWidth, 200f, style);
            var aboutTextHeight = aboutTextArea.GetRequiredHeight() + 5f;

            var disclaimerTextArea = new FormattedTextArea(disclaimerText, 0.0f, 0.0f, disclaimerTextWidth, 200f, style);
            var disclaimerTextHeight = disclaimerTextArea.GetRequiredHeight() + 5f;



            page.Elements.Add(new FormattedTextArea(aboutTitle, _report.LeftMargin, top, aboutTextWidth, _sectionNameFontSize + 2f, Fonts.CalibriFontFamily, _sectionNameFontSize, false));
            page.Elements.Add(new FormattedTextArea(disclaimerTitle, _report.LeftMargin + 220f, top, aboutTextWidth, _sectionNameFontSize + 2f, Fonts.CalibriFontFamily, _sectionNameFontSize, false));

            top += 25f;

            page.Elements.Add(new FormattedTextArea(aboutText, _report.LeftMargin, top, aboutTextWidth, aboutTextHeight, style));
            page.Elements.Add(new FormattedTextArea(disclaimerText, _report.LeftMargin + 220f, top, disclaimerTextWidth, disclaimerTextHeight, style));

            top += aboutTextHeight;

            const string text = "www.sustainalytics.com";
            var font = Fonts.CalibriBold;

            var label = new Label(text, _report.LeftMargin, top, 100f, 10f, font, 8f, RgbColor.Black);

            var action = new UrlAction("www.sustainalytics.com");
            var link = new Link(_report.LeftMargin, top, font.GetTextWidth(text, 8f), 10f, action);

            var elements = new Group { label, link };

            page.Elements.Add(elements);

        }

        private class ProductInvolvementIndicator
        {
            public string Symbol { get; set; }
            public string Name { get; set; }
            public string Description { get; set; }
            public bool Enabled { get; set; }
            public DateTime LastEdited { get; set; }
        }

        private List<ProductInvolvementIndicator> GetHighImpactFossilFuelInvolvementIndicators()
        {
            return GetProductInvolvementIndicators(true);
        }

        private List<ProductInvolvementIndicator> GetProductInvolvementIndicators(bool isHiffiIndicators = false)
        {
            var indicatorSymbols = new[] { "CW", "MC", "AT", "AL", "GB", "GM", "PE", "TP" };

            if (isHiffiIndicators)
            {
                indicatorSymbols = new[] { "TC", "OS" };
            }


            var indicators = new List<ProductInvolvementIndicator>();

            var indicatorTemplates = _dataSource.ProductInvolvementIndicators
                .Where(pi => indicatorSymbols.Contains(pi.Symbol))
                .OrderBy(pi => pi.Symbol)
                .ToList();


            foreach (var indicatorTemplate in indicatorTemplates)
            {
                var assessments = new List<ProductInvolvementDataIndicator>();

                if (_dataSource.ProductInvolvementCompanyAssessment != null)
                {
                    assessments = _dataSource.ProductInvolvementCompanyAssessment
                        .Indicators.OrderBy(a => a.CategorySymbol)
                        .Where(a => a.IndicatorSymbol == indicatorTemplate.Symbol)
                        .ToList();
                }

                var enabled = false;
                var description =
                    $"<p><font color='#000000' PointSize=10><b>{indicatorTemplate.Name}</font></b></p>";

                if (assessments.Any())
                {
                    assessments.ForEach(assessment =>
                    {
                        var categoryIndicatorTemplate = indicatorTemplate?.Categories
                            .SingleOrDefault(c => c.Symbol == assessment.CategorySymbol);

                        if (categoryIndicatorTemplate == null) return;


                        description += $"<p>{categoryIndicatorTemplate.Description}</p>";
                        enabled = true;

                        var rangeOrOwnership = string.Empty;

                        if (assessment.HasLevelOfInvolvement
                            && !assessment.LevelOfInvolvement.Start.NearlyEq(-1.00)
                            && !assessment.LevelOfInvolvement.Stop.NearlyEq(-1.00))
                        {
                            rangeOrOwnership += $"Range: {assessment.LevelOfInvolvement.Text}";
                        }

                        if (assessment.HasOwnershipPercent && assessment.OwnershipPercent > 0)
                        {
                            if (!string.IsNullOrEmpty(rangeOrOwnership))
                                rangeOrOwnership += ", ";

                            rangeOrOwnership += "Ownership: " + assessment.OwnershipPercent*100 + "%";
                        }

                        description += $"<p>{rangeOrOwnership}</p>";
                    });

                    indicators.Add(new ProductInvolvementIndicator
                    {
                        Symbol = indicatorTemplate.Symbol,
                        Name = indicatorTemplate.Name,
                        Description = description,
                        Enabled = enabled,
                        LastEdited = assessments.Max(a => a.LastEdited)
                    });
                }
                else
                {
                    description = $"<p><b>{indicatorTemplate.Name}</b></p><p>No evidence of involvement</p>";

                    indicators.Add(new ProductInvolvementIndicator
                    {
                        Symbol = indicatorTemplate.Symbol,
                        Name = indicatorTemplate.Name,
                        Description = description,
                        Enabled = false,
                        LastEdited = DateTime.MinValue
                    });
                }
            }

            return indicators.OrderByDescending(i => i.Enabled).ThenBy(i => i.Name).ToList();
        }


        private Table AddProductInvolvement(float top, List<ProductInvolvementIndicator> indicators)
        {
            const float imgColumnWidth = 50f;
            const float textColumnWidth = 130f;
            const float left = 205f;


            var minRowHeight = 70f;


            var tableWidth = 2*(imgColumnWidth + textColumnWidth);
            var tableHeight = 4* minRowHeight + 10;

            var style = new FormattedTextAreaStyle(Fonts.CalibriFontFamily, 8f, false)
            {
                Paragraph = { Align = TextAlign.Justify, Indent = 0, SpacingAfter = 5, AllowOrphanLines = false }
            };

            var table = GetDefaultTable(left, top, tableWidth, tableHeight);

            table.Columns.Add(imgColumnWidth);
            table.Columns.Add(textColumnWidth);
            table.Columns.Add(imgColumnWidth);
            table.Columns.Add(textColumnWidth);

            if (!indicators.Any()) return table;


            var maxRows = indicators.Count/2;
            if (indicators.Count%2 != 0)
            {
                maxRows++;
            }
            var rowHeights = new float[maxRows];
            var maxTextHeight = 0f;
            var index = 0;
            var rowIndex = 0;

            foreach (var indicator in indicators)
            {
                var formattedTextArea = new FormattedTextArea(indicator.Description, 0f, 0f, textColumnWidth - 10, 200f, style);
                var textHeight = (int)formattedTextArea.GetRequiredHeight() + 20f;
                rowHeights[rowIndex] = textHeight;
                index++;

                if (index == 2)
                {
                    if (textHeight > maxTextHeight)
                        maxTextHeight = textHeight;

                    if (maxTextHeight < minRowHeight)
                        maxTextHeight = minRowHeight;

                    rowHeights[rowIndex] = maxTextHeight;

                    rowIndex++;
                    index = 0;
                }
                else
                {
                    maxTextHeight = textHeight;
                }
            }


            tableHeight = rowHeights.Sum() + 5f;

            table = GetDefaultTable(left, top, tableWidth, tableHeight);

            table.Columns.Add(imgColumnWidth);
            table.Columns.Add(textColumnWidth);
            table.Columns.Add(imgColumnWidth);
            table.Columns.Add(textColumnWidth);


            index = 0;
            rowIndex = 0;
            Row row = null;

            foreach (var indicator in indicators)
            {
                if (index == 0)
                    row = table.Rows.Add(rowHeights[rowIndex]);

                if (row != null)
                {
                    row.Cells.Add(new Image(GetIndicatorImage(indicator.Symbol, indicator.Enabled), 0, 0, 0.4f));
                    row.Cells.Add(new FormattedTextArea(indicator.Description, 0f, 0f, textColumnWidth - 10, rowHeights[rowIndex], style));
                }

                index++;

                if (index != 2) continue;

                rowIndex++;
                index = 0;
            }

            return table;
        }


        public ImageData GetIndicatorImage(string indicatorSymbol, bool enabled)
        {
            var strOnOff = enabled ? "on" : "off";
            var imgBytes= Sustainalytics.ReportGeneratorFramework.Utils.GetPngFromSvgImage(string.Format("icn-pi-{0}_{1}.svg", indicatorSymbol, strOnOff));
            return ImageData.GetImage(imgBytes);
        }
    }
}
